<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        const PENDING = 'PENDING'
        const FULFILED = 'FULFILED'
        const REJECTED = 'REJECTED'

        /**
         * 判断是否promise的方法
         * @param {*} promise 
         * @returns 
         */
        const isPromise = promise => {
            if ((typeof promise === 'object' && typeof promise !== null) || typeof promise === 'function') {
                return typeof promise.then === 'function'
            } else {
                return false
            }
        }
        /**
         * 链式调用的核心方法
         * @param {*} promise2 
         * @param {*} x 
         * @param {*} resolve 
         * @param {*} reject 
         * @returns 
         */
        const resolvePromise = (promise2, x, resolve, reject) => {
            // 防止别人写的状态发送突变
            let called;
            if (promise2 === x) {
                return reject(new TypeError(`Chaining cycle detected for promise #<Promise>`))
            }
            if (isPromise(x)) {
                try {
                    const then = x.then
                    then.call(x, y => {
                        // y可能是promise ,也可能是个普通值 递归调用
                        if (called) return
                        called = true
                        resolvePromise(promise2, y, resolve, reject)
                    }, r => {
                        if (called) return
                        called = true
                        reject(r)
                    })
                } catch (error) {
                    if (called) return
                    called = true
                    reject(error)
                }
            } else {
                // 普通值直接返回
                resolve(x)
            }

        }
        class Promise {
            constructor(executor) {
                this.status = PENDING;
                this.value = '';
                this.reason = '';
                this.fulfiledCallbacks = [];
                this.rejectedCallbacks = [];
                const resolve = (value) => {
                    if (value instanceof Promise) {
                        console.log(value.then)
                        console.log(resolve)
                        return value.then(resolve, reject)
                    }
                    if (this.status === PENDING) {
                        this.status = FULFILED
                        this.value = value
                        this.fulfiledCallbacks.forEach(v => v())
                    }
                }
                const reject = (reason) => {
                    if (this.status === PENDING) {
                        this.status = REJECTED
                        this.reason = reason
                        this.rejectedCallbacks.forEach(v => v())
                    }
                }
                try {
                    executor(resolve, reject)
                } catch (error) {
                    console.log(error)
                    reject(error)
                }
            }
            then(onFulfailed, onRejected) {
                // then(data=>{
                //    return data
                //})
                onFulfailed = typeof onFulfailed === 'function' ? onFulfailed : v => v;
                onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
                const promise2 = new Promise((resolve, reject) => {
                    if (this.status === FULFILED) {
                        setTimeout(() => {
                            try {
                                console.log('x=>', x)
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                            const x = onFulfailed(this.value)

                        }, 0)
                    }
                    if (this.status === REJECTED) {
                        // const x = onRejected(this.reason)
                        // resolve(x)
                        setTimeout(() => {
                            try {
                                console.log('1111', this.reason)
                                const x = onRejected(this.reason)
                                console.log('2222', x)
                                resolvePromise(promise2, x, resolve, reject)
                            } catch (error) {
                                console.log('error', error)
                                reject(error)
                            }

                        }, 0)

                    }
                    if (this.status === PENDING) {
                        this.fulfiledCallbacks.push(() => {
                            // TODOS AOP
                            setTimeout(() => {
                                try {
                                    console.log('todo', this.value)
                                    const x = onFulfailed(this.value)
                                    console.log('todo', x)
                                    resolvePromise(promise2, x, resolve, reject)
                                } catch (error) {
                                    console.log('todo', error)
                                    reject(error)
                                }

                            }, 0)
                        })
                        this.rejectedCallbacks.push(() => {
                            // TODOS AOP
                            setTimeout(() => {
                                try {
                                    console.log('todo', this.reason)
                                    const x = onRejected(this.reason)
                                    console.log('todo', x)
                                    resolvePromise(promise2, x, resolve, reject)
                                } catch (error) {
                                    console.log('todo', error)
                                    reject(error)
                                }
                            }, 0)
                        })
                    }
                })
                return promise2
            }
        }
        const p = new Promise((resolve, reject) => {
            resolve(new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(100)
                })
            }))
        }).then().then().then(res => {
            console.log(res)
        }, err => {
            console.log(err, 'err')
        })
    </script>
</body>

</html>